package cn.edu.hnu.gpg.api;

import cn.edu.hnu.gpg.api.error.ApiError;
import cn.edu.hnu.gpg.api.error.FailedPreconditionException;
import cn.edu.hnu.gpg.api.error.InvalidArgumentException;
import cn.edu.hnu.gpg.api.error.UnknownException;
import cn.edu.hnu.gpg.dto.Account;
import cn.edu.hnu.gpg.entity.Student;
import cn.edu.hnu.gpg.entity.Teacher;
import cn.edu.hnu.gpg.entity.enums.UserType;
import cn.edu.hnu.gpg.service.AdminService;
import cn.edu.hnu.gpg.service.StudentService;
import cn.edu.hnu.gpg.service.TeacherService;
import cn.edu.hnu.gpg.service.UserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/api/admin")
public class AdminApiController {

    static private final Logger logger = LogManager.getLogger(AdminApiController.class);

    private final AdminService adminService;
    private final UserService userService;
    private final StudentService studentService;
    private final TeacherService teacherService;

    @Autowired
    public AdminApiController(AdminService adminService, UserService userService, StudentService studentService, TeacherService teacherService) {
        this.adminService = adminService;
        this.userService = userService;
        this.studentService = studentService;
        this.teacherService = teacherService;
    }

    @RequestMapping(value = "add_accounts", method = RequestMethod.POST)
    @ResponseBody
    public void addAccounts(@RequestBody List<Account> accounts) throws InvalidArgumentException, FailedPreconditionException, UnknownException {
        try {
            adminService.createAccounts(accounts);
        } catch (DataAccessException e) {
            throw new FailedPreconditionException(new ApiError(ApiError.ErrorType.ERR_CONSTRAINT_VIOLATION, e.getCause().getMessage()));
        } catch (IllegalStateException e) {
            throw new InvalidArgumentException(e.getMessage());
        } catch (Exception e) {
            throw new UnknownException(e.getMessage());
        }
    }

    @RequestMapping(value = "search_account", method = RequestMethod.GET)
    @ResponseBody
    public List<Account> searchAccounts(@RequestParam UserType userType,
                                        @RequestParam(required = false) String name,
                                        @RequestParam(required = false) String number,
                                        @RequestParam(required = false) String idNumber,
                                        @RequestParam(required = false, defaultValue = "true") Boolean onlyActiveAccount) throws InvalidArgumentException {

        // 严格要求合法参数，防止 SQL 模糊检索时出现错误
        if (name != null && (name.contains("%") || name.contains("_")))
            throw new InvalidArgumentException("\"" + name + "\" contains invalid character");
        if (number != null && !number.matches("[A-Za-z0-9]+"))
            throw new InvalidArgumentException("\"" + number + "\" cannot match the pattern");
        if (idNumber != null && !idNumber.matches("[\\d|X]{0,18}"))
            throw new InvalidArgumentException("\"" + idNumber + "\" cannot match the pattern");

        switch (userType) {
            case S:
                Student student = new Student();
                student.setStudentNumber(number);
                student.setName(name);
                student.setIdNumber(idNumber);

                return studentService.findStudentAccountLike(student, onlyActiveAccount);

            case T:
                Teacher teacher = new Teacher();
                teacher.setTeacherNumber(number);
                teacher.setName(name);
                teacher.setIdNumber(idNumber);

                return teacherService.findTeacherAccount(teacher, onlyActiveAccount);

                default:
                    throw new InvalidArgumentException();
        }
    }

    @RequestMapping(value = "update_account", method = RequestMethod.PUT)
    @ResponseBody
    public void updateAccount(@RequestBody Account account) throws InvalidArgumentException, FailedPreconditionException, UnknownException {
        if (account == null || account.getUserType() == null)
            throw new InvalidArgumentException(new ApiError(ApiError.ErrorType.ERR_MISSING_PARAMETER));

        switch (account.getUserType()) {
            case S:
                if (account.getStudent() == null || account.getStudent().getStudentNumber() == null)
                    throw new InvalidArgumentException(
                            new ApiError(ApiError.ErrorType.ERR_MISSING_PARAMETER,"Student number is required."));

                // 删去禁止更改的属性
                Student student = account.getStudent();
                student.setUserId(null);
                student.setStudentId(null);

                try {
                    boolean success = studentService.updateStudentByStudentNumber(student, true);
                    if (!success)
                        throw new UnknownException("Failed updating account");
                } catch (RuntimeException e) {
                    logger.debug(e.getMessage());
                    throw new FailedPreconditionException("Check your request and try again");
                }
                break;

            case T:
                if (account.getTeacher() == null || account.getTeacher().getTeacherNumber() == null)
                    throw new InvalidArgumentException(
                            new ApiError(ApiError.ErrorType.ERR_MISSING_PARAMETER,"Teacher number is required."));

                // 删去禁止更改的属性
                Teacher teacher = account.getTeacher();
                teacher.setUserId(null);
                teacher.setTeacherId(null);

                try {
                    boolean success = teacherService.updateTeacherByTeacherNumber(teacher, true);
                    if (!success)
                        throw new UnknownException("Failed updating account");
                } catch (RuntimeException e) {
                    logger.debug(e.getMessage());
                    throw new FailedPreconditionException("Check your request and try again");
                }
                break;

            case A:
                if (account.getUserType() == UserType.A)
                    throw new InvalidArgumentException("User type cannot be \"A\"");

        }
    }

    @RequestMapping(value = "inactivate_account", method = RequestMethod.POST)
    @ResponseBody
    public void inactivateAccount(@RequestParam("username") String username) throws InvalidArgumentException {

        if (!userService.inactivateUser(username))
            throw new InvalidArgumentException("Specified a non-existent username");
    }
}
